<!doctype html>
<html>
<head>
<meta charset="utf8">
<link rel="help" href="https://url.spec.whatwg.org/#urlsearchparams">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="resources/testharness-extras.js"></script>
<script>
function assert_type_error(f, msg) {
    assert_throws_js(TypeError, f, msg);
}

// Consider upstreaming something kinda like this.
function assert_iteration_equals(iteration, expectation) {
    var actual = [...iteration];
    assert_equals(actual.length, expectation.length);
    for (var i = 0; i < actual.length; i++) {
        assert_equals(typeof actual[i], typeof expectation[i]);
        if (actual[i] instanceof Array)
            assert_array_equals(actual[i], expectation[i]);
        else
            assert_equals(actual[i], expectation[i]);
    }
}

test(function() {
    var params = new URLSearchParams();
    assert_equals(params + '', '');
    params = new URLSearchParams('');
    assert_equals(params + '', '');
    params = new URLSearchParams('a=b');
    assert_equals(params + '', 'a=b');
    params = new URLSearchParams(params);
    assert_equals(params + '', 'a=b');

    // Leading '?' should be ignored.
    params = new URLSearchParams('?a=b');
    assert_equals(params + '', 'a=b');

    // https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer
    // is performed upon stringification of |params|, requiring that
    // '?' is percent encoded.
    params = new URLSearchParams('??a=b');
    assert_equals(params + '', '%3Fa=b');
    params = new URLSearchParams('?');
    assert_equals(params + '', '');
    params = new URLSearchParams('??');
    assert_equals(params + '', '%3F=');
}, 'Basic URLSearchParams construction');

test(function() {
    assert_type_error(function () { URLSearchParams(); }, 'Failed to construct \'URLSearchParams\': Please use the \'new\' operator, this DOM object constructor cannot be called as a function.');

    var params = new URLSearchParams('');
    assert_not_equals(params, null, 'constructor returned non-null value.');
    assert_equals(params.__proto__, URLSearchParams.prototype, 'expected URLSearchParams.prototype as prototype.');
}, 'URLSearchParams constructor, empty.');

test(function() {
    var params = new URLSearchParams('a=b');
    assert_not_equals(params, null, 'constructor returned non-null value.');
    assert_true(params.has('a'), 'Search params object has name "a"');
    assert_false(params.has('b'), 'Search params object has not got name "b"');

    var params = new URLSearchParams('a=b&c');
    assert_not_equals(params, null, 'constructor returned non-null value.');
    assert_true(params.has('a'), 'Search params object has name "a"');
    assert_true(params.has('c'), 'Search params object has name "c"');

    var params = new URLSearchParams('&a&&& &&&&&a+b=& c&m%c3%b8%c3%b8');
    assert_not_equals(params, null, 'constructor returned non-null value.');
    assert_true(params.has('a'), 'Search params object has name "a"');
    assert_true(params.has('a b'), 'Search params object has name "a b"');
    assert_true(params.has(' '), 'Search params object has name " "');
    assert_false(params.has('c'), 'Search params object did not have the name "c"');
    assert_true(params.has(' c'), 'Search params object has name " c"');
    assert_true(params.has('møø'), 'Search params object has name "møø"');
}, 'URLSearchParams constructor, string.');

test(function() {
    var seed = new URLSearchParams('a=b&c=d');
    var params = new URLSearchParams(seed);
    assert_not_equals(params, null, 'constructor returned non-null value.');
    assert_iteration_equals(params, [ ['a', 'b' ], [ 'c', 'd' ] ]);
    // The name-value pairs are copied when created; later updates
    // should not be observable.
    seed.append('e', 'f');
    assert_false(params.has('e'));
    params.append('g', 'h');
    assert_false(seed.has('g'));
}, 'sequence initializer, object coerced to iterable');

test(function() {
    var params = new URLSearchParams('a=b+c');
    assert_equals(params.get('a'), 'b c');
    params = new URLSearchParams('a+b=c');
    assert_equals(params.get('a b'), 'c');
}, 'Parse +');

test(function() {
    var params = new URLSearchParams('a=b%2Bc');
    assert_equals(params.get('a'), 'b+c');
    params = new URLSearchParams('a%2Bb=c');
    assert_equals(params.get('a+b'), 'c');
}, 'Parse %2B');

test(function() {
    var params = new URLSearchParams('a=b c');
    assert_equals(params.get('a'), 'b c');
    params = new URLSearchParams('a b=c');
    assert_equals(params.get('a b'), 'c');
}, 'Parse space');

test(function() {
    var params = new URLSearchParams('a=b%20c');
    assert_equals(params.get('a'), 'b c');
    params = new URLSearchParams('a%20b=c');
    assert_equals(params.get('a b'), 'c');
}, 'Parse %20');

test(function() {
    var params = new URLSearchParams('a=b\0c');
    assert_equals(params.get('a'), 'b\0c');
    params = new URLSearchParams('a\0b=c');
    assert_equals(params.get('a\0b'), 'c');
}, 'Parse \\0');

test(function() {
    var params = new URLSearchParams('a=b%00c');
    assert_equals(params.get('a'), 'b\0c');
    params = new URLSearchParams('a%00b=c');
    assert_equals(params.get('a\0b'), 'c');
}, 'Parse %00');

test(function() {
    var params = new URLSearchParams('a=b\u2384');
    assert_equals(params.get('a'), 'b\u2384');
    params = new URLSearchParams('a\u2384b=c');
    assert_equals(params.get('a\u2384b'), 'c');
}, 'Parse \u2384');  // Unicode Character 'COMPOSITION SYMBOL' (U+2384)

test(function() {
    var params = new URLSearchParams('a=b%e2%8e%84');
    assert_equals(params.get('a'), 'b\u2384');
    params = new URLSearchParams('a%e2%8e%84b=c');
    assert_equals(params.get('a\u2384b'), 'c');
}, 'Parse %e2%8e%84');  // Unicode Character 'COMPOSITION SYMBOL' (U+2384)

test(function() {
    var params = new URLSearchParams('a=b\uD83D\uDCA9c');
    assert_equals(params.get('a'), 'b\uD83D\uDCA9c');
    params = new URLSearchParams('a\uD83D\uDCA9b=c');
    assert_equals(params.get('a\uD83D\uDCA9b'), 'c');
}, 'Parse \uD83D\uDCA9');  // Unicode Character 'PILE OF POO' (U+1F4A9)

test(function() {
    var params = new URLSearchParams('a=b%f0%9f%92%a9c');
    assert_equals(params.get('a'), 'b\uD83D\uDCA9c');
    params = new URLSearchParams('a%f0%9f%92%a9b=c');
    assert_equals(params.get('a\uD83D\uDCA9b'), 'c');
}, 'Parse %f0%9f%92%a9');  // Unicode Character 'PILE OF POO' (U+1F4A9)

test(function() {
    var params = new URLSearchParams('=');
    assert_equals(params.toString(), '=');
}, 'Parse =');

test(function() {
    let params = new URLSearchParams([]);
    assert_true(params !== null, 'Empty sequence');
    assert_equals(params.toString(), '');

    params = new URLSearchParams([[1, 2], ['a', 'b']]);
    assert_equals(params.toString(), '1=2&a=b');

    assert_type_error(() => { new URLSearchParams([[1, 2, 3]]) },
      "Sequence elements must be pairs");
}, 'sequence initializer');

test(function() {
    let params = new URLSearchParams({});
    assert_true(params !== null, 'Empty record');
    assert_equals(params.toString(), '');

    params = new URLSearchParams({1: 2, 'a': 'b'});
    assert_equals(params.toString(), '1=2&a=b');

    params = new URLSearchParams({false: true, 0: 'foo'});
    assert_equals(params.toString(), '0=foo&false=true');
}, 'record initializer');

</script>
</head>
</html>
